namespace ScrumSprintMonitor.Wpf.Application
{
	#region #using Directives

	using System;
	using System.Diagnostics.CodeAnalysis;
	using System.IO;
	using System.Linq;
	using System.Reflection;

	using Extensibility;

	using Infrastructure;
	using Infrastructure.Configuration;

	using Microsoft.Practices.ServiceLocation;

	#endregion

	public class AddInHost<TAddIn> : IAddInHost<TAddIn>
		where TAddIn : ScrumSprintMonitorAddIn
	{
		private readonly ILogger<AddInHost<TAddIn>> logger;
		private readonly IServiceLocator serviceLocator;
		private string outputDirectory;

		[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures",
			Justification = "The IoC container takes care of generating these complex argument lists for us")]
		public AddInHost(IServiceLocator serviceLocator, ILogger<AddInHost<TAddIn>> logger)
		{
			this.serviceLocator = serviceLocator;
			this.logger = logger;
		}

		#region IAddInHost<TAddIn> Members

		public string GetOutputDirectory()
		{
			if (string.IsNullOrEmpty(this.outputDirectory))
			{
				this.outputDirectory = TryCreateOutputDirectory(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location));
				if (string.IsNullOrEmpty(this.outputDirectory))
				{
					this.outputDirectory =
						TryCreateOutputDirectory(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Scrum Sprint Monitor"));
				}
			}

			return this.outputDirectory;
		}

		#endregion

		private string TryCreateOutputDirectory(string basePath)
		{
			var sprintConfiguration = this.serviceLocator.GetInstance<ISprintConfiguration>();
			string actualDirectory = Path.Combine(Path.Combine(basePath, "Output"), sprintConfiguration.ProjectName);

			string addInName = null;
			var attribute = typeof(TAddIn).GetCustomAttributes(typeof(AddInAttribute), true).FirstOrDefault() as AddInAttribute;
			if (attribute != null)
			{
				addInName = attribute.Name;
			}

			if (!string.IsNullOrEmpty(addInName))
			{
				actualDirectory = Path.Combine(actualDirectory, addInName);
			}

			if (!Directory.Exists(actualDirectory))
			{
				this.logger.Info("Trying to create output folder '{0}'...", actualDirectory);

				bool createdDirectory = false;
				try
				{
					Directory.CreateDirectory(actualDirectory);

					createdDirectory = true;
				}
				catch (UnauthorizedAccessException uae)
				{
					// Ignore
					this.logger.Warn(uae, "Failed 1st attempt to create output folder at '{0}'.", actualDirectory);
				}
				catch (Exception e)
				{
					this.logger.Fatal(e, "Failed attempt to create output folder at '{0}'.", actualDirectory);

					throw;
				}
				finally
				{
					if (createdDirectory)
					{
						this.logger.Info("Created output folder '{0}'.", actualDirectory);
					}
					else
					{
						actualDirectory = null;
					}
				}
			}

			return actualDirectory;
		}
	}
}